<!DOCTYPE html>
<html lang="zh-CN">

<!-- Head tag -->
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  
  <meta name="baidu-site-verification" content="undefined" />
  <meta name="google-site-verification" content="undefined" />
  <meta name="msvalidate.01" content="undefined" />
  <meta name="360-site-verification" content="undefined" />
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <!-- if page has tags, then add tags to keyword -->
  
  
  <meta name="keywords" content=",Golang底层,Golang,">
  <!-- page.description has higher priority -->
  <meta name="description" content="Golang程序编译执行流程，如何从源代码一步步转为可执行文件">
  <link rel="shortcut icon" href="/blog/img/logo.webp">
  <title>
    
    Golang程序编译执行流程 | Evrn
    
  </title>

  <link rel="canonical" href="/blog/post/go-how-to-build.html">
  
<link rel="stylesheet" href="/blog/css/reset.css">

  
<link rel="stylesheet" href="/blog/css/layout.css">

  
<link rel="stylesheet" href="/blog/css/markdown.css">

  <!-- Pygments Highlight CSS -->
  
<link rel="stylesheet" href="/blog/css/highlight.css">

  
<link rel="stylesheet" href="/blog/css/geektutu.css">

  <!-- global function -->
  <script>
    window.globalAddScript = function (url, onload, onerror) {
      var s = document.createElement('script');
      s.src = url;
      onload && (s.onload = onload);
      onerror && (s.onerror = onerror);
      document.body.appendChild(s);
    }
    window.globalAddCss = function (url) {
      var s = document.createElement('link');
      s.rel = 'stylesheet';
      s.href = url;
      document.body.appendChild(s);
    }
    window.getPosition = function (ele) {
      var x = 0, y = 0;
      while (ele) {
        x += (ele.offsetLeft - ele.scrollLeft + ele.clientLeft);
        y += (ele.offsetTop - ele.scrollTop + ele.clientTop);
        ele = ele.offsetParent;
      }
      return { x: x, y: y };
    }
    window.getDom = function (str) { return document.querySelector(str) }
  </script>
  <!-- google ad -->
  
<meta name="generator" content="Hexo 4.2.0"></head>

<body>
    <header class="gkt-header col-xs-12 padding-0">
    <div id="gkt-nav" class="gkt-header-container">
        <a href="/blog/" class="gkt-header-title float-left">
            <img class="float-left" src="/blog/img/logo.webp" alt="">
            <span>Evrn</span>
        </a>
        <nav class="gkt-header-nav text-right">
            <ul>
                <li><a class="hidden-xs" href="/blog/">主页</a></li>
                <li><a href="/blog/series/">专题</a></li>
                <li><a href="/blog/archives/">归档</a></li>
                <li><a href="/blog/post/link.html">友链</a></li>
                <li><a href="/blog/post/about.html">留言</a></li>
            </ul>
        </nav>
    </div>
    <div id="gkt-cate-nav" class="gkt-header-container hidden-xs">
        
        <nav class="gkt-header-nav float-left">
            <ul>
                
                
                <li class="gkt-cate-name float-left ">
                    <a class="float-left" href="/blog/post/go-learn-interface.html">Golang学习</a>
                    
                </li>
                
                <li class="gkt-cate-name float-left ">
                    <a class="float-left" href="/blog/post/go-asm-indroduce.html">Golang汇编</a>
                    
                </li>
                
                <li class="gkt-cate-name float-left ">
                    <a class="float-left" href="/blog/post/tcp-three-way-handshake.html">基础</a>
                    
                </li>
                
                <li class="gkt-cate-name float-left active">
                    <a class="float-left" href="/blog/post/go-how-to-build.html">Golang底层</a>
                    
                </li>
                
                <li class="gkt-cate-name float-left ">
                    <a class="float-left" href="/blog/post/mysql-index-use.html">MySQL</a>
                    
                </li>
                
            </ul>
        </nav>
    </div>
</header>
<div style="height: 44px"></div>
<div class="hidden-xs" style="height: 44px"></div>
<script>
    (function () {
        window.addEventListener('scroll', function () {
            if (window.innerWidth < 768) {
                return;
            }
            var nav = document.querySelector('#gkt-nav');
            var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
            scrollTop > 50 && (nav.classList.add('hide'));
            scrollTop <= 50 && (nav.classList.remove('hide'));
        });
        var cateNavs = document.querySelectorAll('#gkt-cate-nav>nav>ul>li');
        [].slice.call(cateNavs).forEach(function (item) {
            var sub = item.querySelector('.gkt-sub-cate');
            if (!sub) return;
            item.addEventListener('mouseenter', function (e) { sub.style.display = 'block'; }, false);
            item.addEventListener('mouseleave', function (e) { sub.style.display = 'none'; }, false);
        })
    })();
</script>
    <!-- Main Content -->
    <div class="main-container">
        <!-- Main Content -->
<main class="col-xs-12 padding-0 markdown-it">
    <!-- Post Container -->
    
    
    <!-- Post Content -->
<div class="float-left post-container box-shadow">
    <div class="u-arrow-wrapper hidden-xs">
        
        <a class="float-left" href="/blog/post/mysql-index-use.html"><i class="u-arrow-left"></i></a>
        
        
        <a class="float-right" href="/blog/post/mysql-index-base.html"><i class="u-arrow-right"></i></a>
        
    </div>
    <article class="col-xs-12">
        <h1> Golang程序编译执行流程 </h1>

        

        
        <div class="hidden-lg hidden-md series_links">
            <p> <strong> Golang底层系列文章链接：</strong></p>
            <ul>
                
                <li>
                    <a href="/blog/post/go-how-to-build.html">Golang程序编译执行流程</a>
                    <span class="post-item-date">(Mar 1, 2020)</span>
                </li>
                
            </ul>
        </div>
        
        
        
        <h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><p>go从源代码到可执行目标文件转化过程</p>
<p><img src="http://river.marvelthanos.com/blog/20200308/cofQH8lQfxkg.png?imageslim" alt="mark"></p>
<h2 id="编译器"><a href="#编译器" class="headerlink" title="编译器"></a>编译器</h2><p>go编译器主要完成的工作是</p>
<h3 id="词法与语法分析"><a href="#词法与语法分析" class="headerlink" title="词法与语法分析"></a><a href="https://draveness.me/golang/docs/part1-prerequisite/ch02-compile/golang-lexer-and-parser/" target="_blank" rel="noopener">词法与语法分析</a></h3><h4 id="词法分析"><a href="#词法分析" class="headerlink" title="词法分析"></a>词法分析</h4><p>对源代码进行文本分析，将文件中的字符串序列转换成 Token 序列，方便后面的处理和解析，我们一般会把执行词法分析的程序称为词法解析器（lexer）。</p>
<p>得到：</p>
<p>一个不包含空格、换行等字符的 <strong>Token 序列</strong>（数组），例如：<code>package</code>, <code>json</code>, <code>import</code>, <code>(</code>, <code>io</code>, <code>)</code>, …</p>
<p><img src="http://river.marvelthanos.com/blog/20200308/aJkFNUhMGv6v.png?imageslim" alt="mark"></p>
<p>一个Token通常包括 Token类型，Token值。</p>
<h4 id="语法分析（AST转换）"><a href="#语法分析（AST转换）" class="headerlink" title="语法分析（AST转换）"></a>语法分析（AST转换）</h4><p>它会将词法分析出来的数组转化抽象语法树（AST），抽象语法树抹去了源代码中不重要的一些字符 - 空格、分号或者括号等等。<strong>每一个 AST 都对应着一个单独的 Go 语言文件</strong>，这个抽象语法树中包括当前文件属于的包名、定义的常量、结构体和函数等</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">"json.go"</span>: SourceFile &#123;</span><br><span class="line">    PackageName: <span class="string">"json"</span>,</span><br><span class="line">    ImportDecl: []Import&#123;</span><br><span class="line">        <span class="string">"io"</span>,</span><br><span class="line">    &#125;,</span><br><span class="line">    TopLevelDecl: ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<blockquote>
<p>Go 语言的语法解析器使用的就是 LALR(1)<a href="https://draveness.me/golang/docs/part1-prerequisite/ch02-compile/golang-compile-intro/#fn:6" target="_blank" rel="noopener">6</a> 的文法，对解析器文法感兴趣的读者可以在推荐阅读中找到编译器文法的相关资料。</p>
</blockquote>
<p><img src="http://river.marvelthanos.com/blog/20200308/JJ48lHlhKh3j.png?imageslim" alt="mark"></p>
<p><img src="http://river.marvelthanos.com/blog/20200308/WVUdIoTiAGlp.png?imageslim" alt="mark"></p>
<h3 id="类型检查"><a href="#类型检查" class="headerlink" title="类型检查"></a><a href="https://draveness.me/golang/docs/part1-prerequisite/ch02-compile/golang-typecheck/" target="_blank" rel="noopener">类型检查</a></h3><p>Go 语言的编译器会对语法树中定义和使用的类型进行检查，类型检查分别会按照以下的顺序对不同类型的节点进行验证和处理：</p>
<ol>
<li>常量、类型和函数名及类型；</li>
<li>变量的赋值和初始化；</li>
<li>函数和闭包的主体；</li>
<li>哈希键值对的类型；</li>
<li>导入函数体；</li>
<li>外部的声明；</li>
</ol>
<p>通过对<strong>整颗抽象语法树的遍历</strong>，我们在每一个节点上都会对当前子树的类型进行验证，以保证当前节点上不会出现类型错误的问题，所有的类型错误和不匹配都会在这一个阶段被发现和暴露出来，结构体是否实现了某些接口也会在这一阶段被检查出来。</p>
<p>类型检查阶段不止会对节点的类型进行验证，还会展开和改写一些内建的函数，例如 <code>make</code> 关键字在这个阶段会根据子树的结构被替换成 <code>makeslice</code> 或者 <code>makechan</code> 等函数。</p>
<p><img src="https://img.draveness.me/2019-12-20-15768548776677-golang-keyword-make.png" alt="golang-keyword-make"></p>
<p><strong>类型检查阶段对 make 进行改写</strong></p>
<p><img src="http://river.marvelthanos.com/blog/20200308/H1DQhTeXhlkm.png?imageslim" alt="mark"></p>
<p><strong>关键字和操作符和运行时函数的映射</strong></p>
<p>类型检查这一过程在整个编译流程中还是非常重要的，Go 语言的很多关键字都依赖类型检查期间的展开和改写。</p>
<h3 id="通用-SSA-生成"><a href="#通用-SSA-生成" class="headerlink" title="通用 SSA 生成"></a><a href="https://draveness.me/golang/docs/part1-prerequisite/ch02-compile/golang-ir-ssa/" target="_blank" rel="noopener">通用 SSA 生成</a></h3><p>将输入的抽象语法树转换成使用了 SSA 特性的中间代码。Go 语言的编译器会使用 <a href="https://github.com/golang/go/blob/06b12e660c239541c973ea9340f00455b9c5a266/src/cmd/compile/internal/gc/pgen.go#L297-L326" target="_blank" rel="noopener"><code>compileSSA</code></a> 函数将抽象语法树转换成中间代码，我们可以先看一下该函数的简要实现：</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">compileSSA</span><span class="params">(fn *Node, worker <span class="keyword">int</span>)</span></span> &#123;</span><br><span class="line">	f := buildssa(fn, worker)</span><br><span class="line">	pp := newProgs(fn, worker)</span><br><span class="line">	genssa(f, pp)</span><br><span class="line"></span><br><span class="line">	pp.Flush()</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><code>buildssa</code> 就是用来具有 SSA 特性的中间代码的函数，</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">buildssa</span><span class="params">(fn *Node, worker <span class="keyword">int</span>)</span> *<span class="title">ssa</span>.<span class="title">Func</span></span> &#123;</span><br><span class="line">	name := fn.funcname()</span><br><span class="line">	<span class="keyword">var</span> astBuf *bytes.Buffer</span><br><span class="line">	<span class="keyword">var</span> s state</span><br><span class="line"></span><br><span class="line">	fe := ssafn&#123;</span><br><span class="line">		curfn: fn,</span><br><span class="line">		log:   printssa &amp;&amp; ssaDumpStdout,</span><br><span class="line">	&#125;</span><br><span class="line">	s.curfn = fn</span><br><span class="line"></span><br><span class="line">	s.f = ssa.NewFunc(&amp;fe)</span><br><span class="line">	s.config = ssaConfig</span><br><span class="line">	s.f.Type = fn.Type</span><br><span class="line">	s.f.Config = ssaConfig</span><br><span class="line">	</span><br><span class="line">	...</span><br><span class="line"></span><br><span class="line">	s.stmtList(fn.Func.Enter)</span><br><span class="line">	s.stmtList(fn.Nbody)</span><br><span class="line"></span><br><span class="line">	ssa.Compile(s.f)</span><br><span class="line">	<span class="keyword">return</span> s.f</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><code>ssaConfig</code> 包含了与 CPU 架构相关的函数和配置，随后的中间代码生成其实也分成两个阶段，第一个阶段是使用 <code>stmtList</code> 以及相关函数将抽象语法树转换成中间代码，第二个阶段会调用 <a href="https://github.com/golang/go/tree/master/src/cmd/compile/internal/ssa" target="_blank" rel="noopener"><code>src/cmd/compile/internal/ssa</code></a> 包的 <a href="https://github.com/golang/go/blob/4d5bb9c60905b162da8b767a8a133f6b4edcaa65/src/cmd/compile/internal/ssa/compile.go#L29-L155" target="_blank" rel="noopener"><code>Compile</code></a> 函数对 SSA 中间代码进行多轮的迭代和转换。</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">Compile</span><span class="params">(f *Func)</span></span> &#123;</span><br><span class="line">	<span class="keyword">if</span> f.Log() &#123;</span><br><span class="line">		f.Logf(<span class="string">"compiling %s\n"</span>, f.Name)</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	phaseName := <span class="string">"init"</span></span><br><span class="line"></span><br><span class="line">	<span class="keyword">for</span> _, p := <span class="keyword">range</span> passes &#123;</span><br><span class="line">		f.pass = &amp;p</span><br><span class="line">		p.fn(f)</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	phaseName = <span class="string">""</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这是删除了很多打印日志和性能分析功能的 <code>Compile</code> 函数，SSA 需要经历的多轮处理也都保存在了 <code>passes</code> 变量中，这个变量中存储了每一轮处理的名字、使用的函数以及表示是否必要的 <code>required</code> 字段：</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> passes = [...]pass&#123;</span><br><span class="line">	&#123;name: <span class="string">"number lines"</span>, fn: numberLines, required: <span class="literal">true</span>&#125;,</span><br><span class="line">	&#123;name: <span class="string">"early phielim"</span>, fn: phielim&#125;,</span><br><span class="line">	&#123;name: <span class="string">"early copyelim"</span>, fn: copyelim&#125;,</span><br><span class="line">	...</span><br><span class="line">	&#123;name: <span class="string">"loop rotate"</span>, fn: loopRotate&#125;,</span><br><span class="line">	&#123;name: <span class="string">"stackframe"</span>, fn: stackframe, required: <span class="literal">true</span>&#125;,</span><br><span class="line">	&#123;name: <span class="string">"trim"</span>, fn: trim&#125;,</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>可以使用 <code>GOSSAFUNC</code> 环境变量构建自己代码并获取从源代码到最终的中间代码经历的几十次迭代，所有的数据都被存储到了 <code>ssa.html</code> 文件中：</p>
<p>如代码：</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"># .\asm1.<span class="keyword">go</span></span><br><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">add</span><span class="params">(a, b <span class="keyword">int</span>)</span> <span class="title">int</span></span> &#123;</span><br><span class="line">	<span class="keyword">return</span> a+b</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">	add(<span class="number">10</span>, <span class="number">32</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">set GOSSAFUNC=add //add是函数名</span><br><span class="line">go build asm1.go</span><br></pre></td></tr></table></figure>

<p>打开ssa.html可以看到</p>
<p><img src="http://river.marvelthanos.com/blog/20200308/VTaHLL2POLaC.png?imageslim" alt="mark"></p>
<p>最终生成了中间代码，plan9汇编</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">TEXT	&quot;&quot;.add(SB)</span><br><span class="line">FUNCDATA	$0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)</span><br><span class="line">FUNCDATA	$1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)</span><br><span class="line">FUNCDATA	$3, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)</span><br><span class="line">PCDATA	$2, $0</span><br><span class="line">PCDATA	$0, $0</span><br><span class="line">MOVQ	$0, &quot;&quot;.~r2+16(SP)</span><br><span class="line">MOVQ	&quot;&quot;.a(SP), AX</span><br><span class="line">ADDQ	&quot;&quot;.b+8(SP), AX</span><br><span class="line">MOVQ	AX, &quot;&quot;.~r2+16(SP)</span><br><span class="line">RET</span><br><span class="line">END</span><br></pre></td></tr></table></figure>

<h2 id="汇编器"><a href="#汇编器" class="headerlink" title="汇编器"></a>汇编器</h2><p>汇编器是将汇编语言翻译为机器语言的程序，Go 语言的汇编器是<strong>基于 Plan 9 汇编器</strong>的输入类型设计的。</p>
<h3 id="机器代码生成"><a href="#机器代码生成" class="headerlink" title="机器代码生成"></a><a href="https://draveness.me/golang/docs/part1-prerequisite/ch02-compile/golang-machinecode/" target="_blank" rel="noopener">机器代码生成</a></h3><p>机器码的生成过程其实就是对 SSA 中间代码的降级（lower）过程，在 SSA 中间代码降级的过程中，编译器将一些值重写成了目标 CPU 架构的特定值，降级的过程处理了所有机器特定的重写规则并对代码进行了一定程度的优化；</p>
<p>在 SSA 中间代码生成阶段的最后，Go 函数体的代码会被转换成一系列的 <code>obj.Prog</code> 结构体。</p>
<p>Go 语言源代码的 <a href="https://github.com/golang/go/tree/master/src/cmd/compile/internal" target="_blank" rel="noopener"><code>src/cmd/compile/internal</code></a> 目录中包含了很多机器码生成相关的包，不同类型的 CPU 分别使用了不同的包生成机器码，其中包括 amd64、arm、arm64、mips、mips64、ppc64、s390x、x86 和 wasm</p>
<p><img src="http://river.marvelthanos.com/blog/20200308/s7dBv9wqaR8y.png?imageslim" alt="mark"></p>
<h2 id="链接"><a href="#链接" class="headerlink" title="链接"></a>链接</h2><p>链接过程就是要把编译器生成的一个个目标文件链接成<strong>可执行文件</strong>。最终得到的文件是分成各种段的，比如数据段、代码段、BSS段等等，运行时会被装载到内存中。各个段具有不同的读写、执行属性，保护了程序的安全运行。</p>
<h2 id="拓展"><a href="#拓展" class="headerlink" title="拓展"></a>拓展</h2><h3 id="ssa是什么"><a href="#ssa是什么" class="headerlink" title="ssa是什么"></a><a href="https://ggaaooppeenngg.github.io/zh-CN/2017/01/22/SSA%E6%98%AF%E4%BB%80%E4%B9%88/" target="_blank" rel="noopener">ssa是什么</a></h3><p><a href="https://en.wikipedia.org/wiki/Static_single_assignment_form" target="_blank" rel="noopener">wiki</a></p>
<p>SSA 代表 static single-assignment，是一种IR(中间表示代码)，将代码转换成一种中间代码，这个中间代码要保证每个变量只被赋值一次。</p>
<p><strong>为了给以后编译器做代码优化做准备.</strong></p>
<p>为了保证变量只被赋值一次，针对同一变量多次赋值，增加了“代”的概念，每次被赋值都是新的一代</p>
<p>比如</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">y := <span class="number">1</span></span><br><span class="line">y := <span class="number">2</span></span><br><span class="line">x := y</span><br></pre></td></tr></table></figure>

<p>转换成</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">y1 := <span class="number">1</span></span><br><span class="line">y2 := <span class="number">2</span></span><br><span class="line">x1 := y1</span><br></pre></td></tr></table></figure>

<p>可以很容易看出x1是从y2来的，上面的代码，y1其实是<strong>多余</strong>的，<strong>后期编译器</strong>就可以把这句<strong>删掉</strong>。</p>
<p>再来例子，假设有这个流程图的代码</p>
<p><img src="http://river.marvelthanos.com/blog/20200308/jUJxWMA649QM.png?imageslim" alt="mark"></p>
<p>经过SSA转换后可得出</p>
<p><img src="http://river.marvelthanos.com/blog/20200308/SkNjXCxHdMLd.png?imageslim" alt="mark"></p>
<p>Φ表示y3会从y1，y2里面取值</p>
<p>从中可以清楚看出每个变量的值传递链</p>
<p>静态单一赋值会有以下几点优势：</p>
<ul>
<li>当每个变量只有一个定值时，数据流分析和优化算法可以变得更简单。</li>
<li>如果一个变量有 N 个使用和 N 个定值，代码中可能是n+m条语句。表示定值-使用链所需要空间（和时间）和 N·M 成正比——即成平方增大。</li>
<li>静态单赋值形式中，变量的使用和定值可以与控制流图的必经节点结构进行联系，从而简化诸如冲突图等算法。</li>
<li>源程序中的同一个变量的不相关使用在 SSA 形式中变成了不同的变量，从而<strong>删除了它们之间不必要的关系</strong>。</li>
</ul>
<p>比如<code>v = φ(c1,c2,...,cn)</code>这种格式，如果c都是相等的可以直接替换成c，或者<code>v=c</code>，如果c是常量的话也可以直接替换掉。在做这些的同时也可以做其他的优化，能够在一趟遍历中都完成，比如<code>copy propagation</code>，<code>x=y</code>或者<code>x=φ(y)</code>都可以直接用y替换掉x。比如<code>constant folding</code>，<code>x=a+b</code>如果<code>a+b</code>是常量的话，可以直接用常量赋值。</p>
<p>在中间代码中使用 SSA 的特性能够为整个程序实现以下的优化：</p>
<ol>
<li><p>常数传播（constant propagation）</p>
</li>
<li><p>值域传播（value range propagation）</p>
</li>
<li><p>稀疏有条件的常数传播（sparse conditional constant propagation）</p>
</li>
<li><p>消除无用的程式码（dead code elimination）</p>
</li>
<li><p>全域数值编号（global value numbering）</p>
</li>
<li><p>消除部分的冗余（partial redundancy elimination）</p>
</li>
<li><p>强度折减（strength reduction）</p>
</li>
<li><p>寄存器分配（register allocation）</p>
</li>
</ol>
<h3 id="抽象语法树（AST）"><a href="#抽象语法树（AST）" class="headerlink" title="抽象语法树（AST）"></a>抽象语法树（AST）</h3><p><a href="https://en.wikipedia.org/wiki/Abstract_syntax_tree" target="_blank" rel="noopener">抽象语法树</a>（AST），是源代码语法的结构的一种抽象表示，它用树状的方式表示编程语言的语法结构<a href="https://draveness.me/golang/docs/part1-prerequisite/ch02-compile/golang-compile-intro/#fn:1" target="_blank" rel="noopener">1</a>。抽象语法树中的每一个节点都表示源代码中的一个元素，每一颗子树都表示一个语法元素，例如一个 if else 语句，我们可以从 <code>2 * 3 + 7</code> 这一表达式中解析出下图所示的抽象语法树。</p>
<p><img src="http://river.marvelthanos.com/blog/20200308/tOBi4taOCr01.png?imageslim" alt="mark"></p>
<p><strong>简单表达式的抽象语法树</strong></p>
<p><img src="http://river.marvelthanos.com/blog/20200308/dXqnrTKohWHg.png?imageslim" alt="mark"></p>
<p>作为编译器常用的数据结构，抽象语法树抹去了源代码中不重要的一些字符 - 空格、分号或者括号等等。编译器在执行完语法分析之后会输出一个抽象语法树，这个抽象语法树会辅助编译器进行语义分析，我们可以用它来确定语法正确的程序是否存在一些类型不匹配或不一致的问题。</p>
<p>参考文章：</p>
<p>Go 程序是怎样跑起来的-码农桃花源</p>
<p><a href="https://draveness.me/golang/docs/part1-prerequisite/ch02-compile/golang-compile-intro" target="_blank" rel="noopener">Go语言编译过程概述</a></p>

        
<script src="/blog/js/qrious.min.js"></script>


        <hr>
        <div>
            
            <p>
                <span>专题: </span>
                
                <a href="/blog/series/#Golang底层">
                    <code key="Golang底层" class="post-label">Golang底层</code>
                </a>
                
            </p>
            
            <p>
                <span>本文发表于 2020-03-01，最后修改于 2020-03-10。</span>
            </p>
            <!-- 文章末尾的提示 start -->
            
            
            
                
            <!-- 文章末尾的提示 end -->
        </div>
        <hr />
        <p name="pagination" style="font-size: 1.2em">
    
    <a class="float-left" href="/blog/post/mysql-index-use.html">上一篇 « 《高性能MySQL》笔记-索引优化</a>
    
    
    <a class="float-right" href="/blog/post/mysql-index-base.html">下一篇 » 《高性能MySQL》笔记-索引基础</a>
    
</p>
    </article>
    <!-- 赞赏 -->
    <!--打赏-->

    <!-- 推荐阅读三篇文章 -->
    <div class="col-xs-12">
        <h3>推荐阅读</h3>
        
        <div class="post-preview">
    <div class="post-img" style="background-image: url('/blog/img/database/database.webp')"></div>
    <div class="post-info">
        <div class="post-info-center">
            <div class="hidden-xs">
                
                
                <span>/</span>
                
                <a class="text-gray" href="/blog/tags/#MySQL"
                    title="MySQL">MySQL</a>
                <span>/</span>
                
                
            </div>
            <a href="/blog/post/mysql-index-use.html" class="title">
                《高性能MySQL》笔记-索引优化
            </a>
            <p class="text-gray">
                <small>
                    <span>发表于2020-03-01，</span>
                    <span class="hidden-xs">全文13286字，</span>
                    <span>阅读约45分钟</span>
                </small>
            </p>
        </div>
    </div>
</div>
        
        <div class="post-preview">
    <div class="post-img" style="background-image: url('/blog/img/tcp/tcp.webp')"></div>
    <div class="post-info">
        <div class="post-info-center">
            <div class="hidden-xs">
                
                
                <span>/</span>
                
                <a class="text-gray" href="/blog/tags/#基础"
                    title="基础">基础</a>
                <span>/</span>
                
                <a class="text-gray" href="/blog/tags/#tcp/http"
                    title="tcp/http">tcp/http</a>
                <span>/</span>
                
                
            </div>
            <a href="/blog/post/tcp-three-way-handshake.html" class="title">
                简述TCP“3次握手，4次挥手”
            </a>
            <p class="text-gray">
                <small>
                    <span>发表于2020-02-21，</span>
                    <span class="hidden-xs">全文9978字，</span>
                    <span>阅读约34分钟</span>
                </small>
            </p>
        </div>
    </div>
</div>
        
        <div class="post-preview">
    <div class="post-img" style="background-image: url('/blog/img/go.webp')"></div>
    <div class="post-info">
        <div class="post-info-center">
            <div class="hidden-xs">
                
                
                <span>/</span>
                
                <a class="text-gray" href="/blog/tags/#Golang"
                    title="Golang">Golang</a>
                <span>/</span>
                
                <a class="text-gray" href="/blog/tags/#Golang源码"
                    title="Golang源码">Golang源码</a>
                <span>/</span>
                
                
            </div>
            <a href="/blog/post/go-learn-interface.html" class="title">
                Go学习 接口和类型断言
            </a>
            <p class="text-gray">
                <small>
                    <span>发表于2019-01-21，</span>
                    <span class="hidden-xs">全文13117字，</span>
                    <span>阅读约44分钟</span>
                </small>
            </p>
        </div>
    </div>
</div>
        
    </div>
    <div class="col-xs-12">
        <!-- 标签列表 -->
        <!-- Featured Tags -->
<style>
    #featured-tag .post-tag-item {
        font-size: 12px;
        line-height: 30px;
        display: inline-block;
        height: 30px;
        margin: 5px 0px;
        padding: 0 7px;
        color: #333;
        border-radius: 15px;
        background: #f6f6f6;
    }

    #featured-tag .post-tag-item:hover {
        color: #337ab7;
    }
</style>
<div id="featured-tag">
    
    <a class="post-tag-item" href="/blog/tags/#关于我" title="关于我"
        rel="1">#关于我 (1) </a>
    
    <a class="post-tag-item" href="/blog/tags/#友链" title="友链"
        rel="1">#友链 (1) </a>
    
    <a class="post-tag-item" href="/blog/tags/#Golang汇编" title="Golang汇编"
        rel="4">#Golang汇编 (4) </a>
    
    <a class="post-tag-item" href="/blog/tags/#Golang底层" title="Golang底层"
        rel="5">#Golang底层 (5) </a>
    
    <a class="post-tag-item" href="/blog/tags/#Golang" title="Golang"
        rel="8">#Golang (8) </a>
    
    <a class="post-tag-item" href="/blog/tags/#Golang基础" title="Golang基础"
        rel="1">#Golang基础 (1) </a>
    
    <a class="post-tag-item" href="/blog/tags/#Golang源码" title="Golang源码"
        rel="2">#Golang源码 (2) </a>
    
    <a class="post-tag-item" href="/blog/tags/#基础" title="基础"
        rel="2">#基础 (2) </a>
    
    <a class="post-tag-item" href="/blog/tags/#tcp/http" title="tcp/http"
        rel="2">#tcp/http (2) </a>
    
    <a class="post-tag-item" href="/blog/tags/#MySQL" title="MySQL"
        rel="3">#MySQL (3) </a>
    
    <a class="post-tag-item" href="/blog/tags/#MySQL优化" title="MySQL优化"
        rel="1">#MySQL优化 (1) </a>
    
</div>
    </div>
    <!-- 评论 -->
    <div class="col-xs-12">
        
    </div>
</div>
<aside class="float-left gkt-sidebar hidden-xs hidden-sm">
    <div style="clear: both"></div>
    <div class="gkt-sidebar-wrapper">
        <section class="box-shadow"><style>
    .gkt-summary {
        border: 1px solid #DDDDDD;
        border-radius: 3px;
        padding: 5px;
        width: 100%;
    }


    .gkt-summary nav {
        overflow: hidden;
    }

    .gkt-summary nav a {
        display: inline-block;
        text-align: center;
        color: #333;
        font-size: 12px;
    }

    .gkt-summary nav span {
        display: block;
    }

    .gkt-summary nav .middle {
        border-left: 1px solid #eaecef;
        border-right: 1px solid #eaecef;
    }

    .gkt-summary .number {
        font-weight: bold;
    }

    .gkt-summary .link-list {
        margin-top: 5px;
        margin-bottom: -5px;
        padding-top: 7px;
        border-top: 1px dashed #999;
        display: flex;
    }

    .gkt-summary .link-list a {
        flex: 1;
    }

    .gkt-summary .link-list img {
        width: 25px;
        height: 25px;
    }
</style>

<div class="gkt-summary">
    <nav>
        <a href="/blog/" class="col-xs-4">
            <number class="number">15</number><span>文章</span>
        </a>
        <a href="/blog/series" class="col-xs-4 middle">
            <number class="number">5</number><span>专题</span>
        </a>
        <a href="/blog/tags" class="col-xs-4">
            <number class="number">11</number><span>标签</span>
        </a>
    </nav>

    
    <div class="link-list">
        
        <a href="https://marvelthanos.com" target="_blank" rel="noopener"><img src="/blog/img/related_links/thanos.webp" /></a>
        
    </div>
    
</div></section>
        
        
        <section class="gkt-sidebar-content box-shadow">
            <strong>Golang底层</strong>
            <ul>
                
                <li>
                    <a href="/blog/post/go-how-to-build.html"
                        class="gkt-sidebar-active">Golang程序编译执行流程</a>
                    
                    <!-- Table of Contents -->
<div id="sidebar-toc">
  <!-- TOC  -->
  
  <ol class="toc-nav"><li class="toc-nav-item toc-nav-level-2"><a class="toc-nav-link" href="#概述"><span class="toc-nav-text">概述</span></a></li><li class="toc-nav-item toc-nav-level-2"><a class="toc-nav-link" href="#编译器"><span class="toc-nav-text">编译器</span></a></li><li class="toc-nav-item toc-nav-level-2"><a class="toc-nav-link" href="#汇编器"><span class="toc-nav-text">汇编器</span></a></li><li class="toc-nav-item toc-nav-level-2"><a class="toc-nav-link" href="#链接"><span class="toc-nav-text">链接</span></a></li><li class="toc-nav-item toc-nav-level-2"><a class="toc-nav-link" href="#拓展"><span class="toc-nav-text">拓展</span></a></li></ol>
  
</div>

<script>
  (function () {
    var h2 = document.querySelectorAll('article h2');
    var h3 = document.querySelectorAll('article h3');
    var linkList = document.querySelectorAll('#sidebar-toc a');

    function findLinkElement(name) {
      for (var i = 0; i < linkList.length; i++) {
        var items = linkList[i].href.split('#');
        if (items && items[items.length - 1] === encodeURIComponent(name)) {
          return i;
        }
      }
      return -1;
    }

    function activeLink(titleList) {
      var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
      for (var i = titleList.length - 1; i >= 0; i--) {
        if (scrollTop - titleList[i].offsetTop > 0) {
          var index = findLinkElement(titleList[i].id);
          index != -1 && linkList[index].classList.add('gkt-sidebar-active');
          break;
        }
      }
    }

    window.addEventListener("scroll", function (e) {
      [].slice.call(linkList).forEach(function (link) {
        link.classList.remove('gkt-sidebar-active');
      })
      activeLink(h2);
    })
  })();
</script>
                    
                </li>
                
            </ul>
        </section>
        
        
        <section class="box-shadow">
            <strong>最近的文章</strong>
            <ul>
                
                <li>
                    <a href="/blog/post/go-learn-lib-sorts.html">Go源码学习 sort包</a>
                </li>
                
                <li>
                    <a href="/blog/post/mysql-optimize-explain.html">EXPLAIN-执行计划查询命令</a>
                </li>
                
                <li>
                    <a href="/blog/post/mysql-index-base.html">《高性能MySQL》笔记-索引基础</a>
                </li>
                
            </ul>
        </section>
        <section class="box-shadow text-center">
            
        </section>
    </div>
</aside>

<script>
    (function () {
        var ele = getDom('.gkt-sidebar-content')
        var wrapper = getDom('.gkt-sidebar-wrapper')
        var last = 0
        window.addEventListener("scroll", function (e) {
            var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
            var isDown = scrollTop > last;
            var pos = getPosition(ele).y - scrollTop;
            var downLimit = 50;
            var upLimit = -100;
            // uarrow.style.marginTop = scrollTop + 'px';
            isDown && pos <= downLimit && wrapper.classList.add("gkt-sidebar-fixed");
            !isDown && pos > upLimit && wrapper.classList.remove("gkt-sidebar-fixed");
            last = scrollTop
        })
    })();
    (function () {
        function resizeUArrow() {
            var s = getDom('.u-arrow-wrapper').style
            var pc = getDom('.post-container')
            s.left = getPosition(pc).x + 'px';
            s.width = pc.clientWidth + 'px';
        }
        resizeUArrow()
        window.addEventListener('resize', resizeUArrow);
    })();
</script>

    
</main>
    </div>
    <style>
    img#go-top {
        position: fixed;
        bottom: 100px;
        width: 50px;
        cursor: pointer;
        z-index: 9999;
    }
</style>
<img id="go-top" src="/blog/icon/top.png" class="hidden-xs" style="display: none" />
<script>
    (function () {
        var goTop = document.getElementById('go-top');
        var mainContainer = document.querySelector('.main-container');
        
        goTop.addEventListener('click', function () {
            window.scroll(0, 0);
        }, false);
        window.addEventListener('scroll', function () {
            var right = document.body.offsetWidth - mainContainer.getBoundingClientRect().right;
            var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
            goTop.style.right = right + 10 + 'px'
            scrollTop > 700 && (goTop.style.display = "block");
            scrollTop <= 700 && (goTop.style.display = "none");
        });
    })();
</script>
    <style>
    #geektutu-click-img-container {
        position: fixed;
        left: 0;
        top: 0;
        text-align: center;
        width: 100%;
        display: none;
        z-index: 9999;
    }

    #geektutu-click-img-container img {
        object-fit: contain;
        background: #eaecef;
        padding: 15px;
        border-radius: 10px;
        height: auto;
        width: auto;
        vertical-align: middle;
    }
</style>


<div id="geektutu-click-img-container">
    <img src="" alt="Big Image">
</div>

<script>
    (function () {
        var container = document.querySelector('#geektutu-click-img-container')
        var targetImg = container.querySelector('img')
        var imgs = document.querySelectorAll('article img');
        targetImg.addEventListener('click', function (e) {
            container.style.display = 'none';
            e && e.stopPropagation && e.stopPropagation();
        }, false);

        for (var i = 0; i < imgs.length; ++i) {
            var img = imgs[i];
            img.addEventListener('click', (function (src, rate) {
                return function (e) {
                    e && e.stopPropagation && e.stopPropagation();
                    if (window.innerWidth < 980) {
                        return
                    }
                    targetImg.style.height = targetImg.style.width = 'auto';
                    if (window.innerWidth / window.innerHeight > rate) {
                        targetImg.style.height = (window.innerHeight - 20) + 'px';
                    } else {
                        targetImg.style.width = (window.innerWidth - 20) + 'px';
                    }
                    container.style.height = window.innerHeight + 'px'
                    container.style.lineHeight = window.innerHeight + 'px'
                    container.style.display = 'block';
                    targetImg.src = src;
                };
            }(img.src, img.width / img.height)), false)
        }
    })();
</script>
    <!-- Footer -->
    <!-- Footer -->
<style>
    footer {
        width: 100%;
        line-height: 1.5;
        padding: 20px;
    }

    footer a {
        color: #333;
        text-decoration: none;
    }

    .footer-hexo img {
        height: 20px;
        margin-bottom: -5px;
    }

    .footer-hexo a {
        color: #337ab7;
    }
</style>
<footer class="text-center col-xs-12">
    <p>
        <small>© 2020 - Evrn - </small>
        <small>
            <a target="_blank" rel="nofollow noopener" href="http://www.miitbeian.gov.cn/"></a>
        </small>
    </p>
    <p class="footer-hexo">
        <!-- 但若直接使用或修改主题，请务必保留这段声明 -->
        <small>Powered by <a target="_blank" href="https://hexo.io">Hexo</a> | Theme
            <a target="_blank" href="https://geektutu.com">Geektutu</a>
            <a href="https://github.com/geektutu/hexo-theme-geektutu" target="_blank" rel="noopener">
                <img src=""
                    alt="Github Star">
            </a>
        </small>
    </p>
    
    <p>
        <small>
            <span id="busuanzi_container_site_pv">👁<span id="busuanzi_value_site_pv"></span></span> &nbsp;
            <span id="busuanzi_container_page_pv">📚<span id="busuanzi_value_page_pv"></span></span>
        </small>
    </p>
    
    

</footer>


<script>
    window.addEventListener('load', function () {
        globalAddScript('//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js');
    })
</script>

<!-- Baidu Tongji -->





    <script>
        // 非本站网址，新开tab页签
        (function () {
            var stopBubble = function (e) {
                e && e.stopPropagation && e.stopPropagation();
            }
            var links = document.querySelectorAll('a');
            [].slice.call(links).forEach(function (item) {
                if (item.href && item.href.indexOf(window.location.host) === -1) {
                    item.target = '_blank'
                }
                // 阻止冒泡，不触发彩蛋。
                item.addEventListener('click', stopBubble, false);
            });
            var article = document.querySelector('article');
            article && article.addEventListener('click', stopBubble, false)
        })();
    </script>
    
</body>

</html>